package org.jivesoftware.smackx.packet;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.util.Iterator;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.EncryptionUtils;
import org.jivesoftware.smackx.provider.EncryptedDataProvider;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.openpgp.PGPCompressedData;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPEncryptedDataList;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPLiteralData;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPOnePassSignatureList;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
import org.spongycastle.openpgp.PGPSecretKeyRingCollection;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.spongycastle.util.io.Streams;
public class EncryptedMessage implements PacketExtension {
private String encryptedText;
@Override
public String getElementName() {
return "x";
}
@Override
public String getNamespace() {
return "jabber:x:encrypted";
}
@Override
public String toXML() {
StringBuilder buf = new StringBuilder();
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\">");
buf.append(encryptedText);
buf.append("</").append(getElementName()).append(">");
return buf.toString();
}
public String getEncryptedText() {
return encryptedText;
}
public String decryptAndGet(String keyPath, String keyPwd) throws Exception {
InputStream in = new ByteArrayInputStream(encryptedText.getBytes());
InputStream keyIn = new BufferedInputStream(new FileInputStream(keyPath));
String out = decrypt(in, keyIn, keyPwd.toCharArray());
keyIn.close();
in.close();
return out;
}
private static String decrypt(InputStream in, InputStream keyIn, char[] passwd) throws IOException, NoSuchProviderException, PGPException {
in = PGPUtil.getDecoderStream(in);
try {
PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
//
// the first object might be a PGP marker packet.
//
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
} else {
enc = (PGPEncryptedDataList) pgpF.nextObject();
}
//
// find the secret key
//
@SuppressWarnings("rawtypes")
Iterator it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn));
while (sKey == null && it.hasNext()) {
pbe = (PGPPublicKeyEncryptedData) it.next();
sKey = EncryptionUtils.findSecretKey(pgpSec, pbe.getKeyID(), passwd);
}
if (sKey == null) {
throw new IllegalArgumentException("secret key for message not found.");
}
InputStream clear = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("SC").build(sKey));
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object message = plainFact.nextObject();
if (message instanceof PGPCompressedData) {
PGPCompressedData cData = (PGPCompressedData) message;
PGPObjectFactory pgpFact = new PGPObjectFactory(cData.getDataStream());
message = pgpFact.nextObject();
}
if (message instanceof PGPLiteralData) {
PGPLiteralData ld = (PGPLiteralData) message;
InputStream unc = ld.getInputStream();
ByteArrayOutputStream fOut = new ByteArrayOutputStream();
Streams.pipeAll(unc, fOut);
byte[] bytes = fOut.toByteArray();
fOut.close();
return new String(bytes);
} else if (message instanceof PGPOnePassSignatureList) {
throw new PGPException("encrypted message contains a signed message - not literal data.");
} else {
throw new PGPException("message is not a simple encrypted file - type unknown.");
}
/*if (pbe.isIntegrityProtected()) {
if (!pbe.verify()) {
System.err.println("message failed integrity check");
} else {
System.err.println("message integrity check passed");
}
} else {
System.err.println("no message integrity check");
}*/
} catch (PGPException e) {
if (e.getUnderlyingException() != null) {
e.getUnderlyingException().printStackTrace();
}
throw e;
}
}
public void setAndEncrypt(String message, String buddyKey) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PGPPublicKey encKey = EncryptionUtils.readPublicKey(buddyKey);
encrypt(out, message, encKey, true, false);
encryptedText = EncryptedDataProvider.removeHeaderFooter(new String(out.toByteArray()));
out.close();
}
private static void encrypt(OutputStream out, String message, PGPPublicKey encKey, boolean armor, boolean withIntegrityCheck) throws IOException, NoSuchProviderException {
out = new ArmoredOutputStream(out);
try {
byte[] bytes = compressFile(message.getBytes(), CompressionAlgorithmTags.ZLIB);
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("SC"));
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encKey).setProvider("SC"));
OutputStream cOut = encGen.open(out, bytes.length);
cOut.write(bytes);
cOut.close();
if (armor) {
out.close();
}
} catch (PGPException e) {
System.err.println(e);
if (e.getUnderlyingException() != null) {
e.getUnderlyingException().printStackTrace();
}
}
}
static byte[] compressFile(byte[] data, int algorithm) throws IOException
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
/*PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(algorithm);
PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY,
new File(fileName));*/
EncryptionUtils.writeToLiteralData(new ByteArrayInputStream(data), bOut, PGPLiteralData.BINARY, "");
byte[] bbytes = bOut.toByteArray();
bOut.close();
return bbytes;
}
public void setEncryptedText(String encryptedText) {
this.encryptedText = encryptedText;
}
}